using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Net;
using System.IO;
using System.Web.Script.Serialization;
using System.Globalization;

namespace IGGCore
{
    /// <summary>
    /// The main scoreboard parser
    /// </summary>
    public class ScoreboardParser
    {
        private RatioCalculator m_calculator;
        private CultureInfo encaCulture = new CultureInfo("en-ca");
        private PlayersParser m_playersParser;

        /// <summary>
        /// Constructor
        /// </summary>
        public ScoreboardParser(PlayersParser playerParser)
        {
            m_calculator = new RatioCalculator();
            m_playersParser = playerParser;
        }

        /// <summary>
        /// Get the raw statisticis from the specified url (html website)
        /// </summary>
        private string GetRawStats(string url)
        {
            StreamReader reader = null;
            string rawStats = null;
            try
            {
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
                reader = new StreamReader(request.GetResponse().GetResponseStream());
                string statsContent = reader.ReadToEnd();
                int startIdx = statsContent.IndexOf("BATTING AVERAGE");
                int EndIdx = statsContent.IndexOf("</pre>");
                rawStats = statsContent.Substring(startIdx, EndIdx - startIdx);
            }
            finally
            {
                if (reader != null)
                {
                    reader.Close();
                }
            }
            return rawStats;
        }


        /// <summary>
        /// Parse the notifications for the specified game
        /// </summary>
        public void ParseNotifications(DateTime currentDate, Game game)
        {
            string id = game.Id.Replace("/", "_");
            id = id.Replace("-", "_");
            string url = CoreConstants.MLB_ONLINE_BASE_URL
                + "/" + ToolBox.GetDateURL(currentDate)
                + "/gid_" + id + "/notifications/" + CoreConstants.NOTIFICATIONS_URL;
            XmlDocument xmldoc = new XmlDocument();
            try
            {
                xmldoc.Load(url);
                int length = xmldoc.InnerText.Length;
            }
            catch (WebException)
            {
                xmldoc = null;
            }
            finally
            {
                xmldoc = null;
            }
        }

        public bool HasChanges()
        {
            bool changed = false;
            WebRequest req = HttpWebRequest.Create("");
            req.Method = "HEAD";
            WebResponse resp = req.GetResponse();
            long ContentLength = long.Parse(resp.Headers.Get("Content-Length"));

            return changed;
        }

        /// <summary>
        /// Parse the online scoreboard and set the game list
        /// </summary>
        public Scoreboard ParseScoreboardFromWebSources(DateTime currentDate, string selectedGameId)
        {
            Scoreboard scoreBoard = new Scoreboard();

            string url = CoreConstants.MLB_ONLINE_BASE_URL + "/" 
                + ToolBox.GetDateURL(currentDate) 
                + "/" + CoreConstants.UBER_SCOREBOARD_URL;
            XmlDocument xmldoc = new XmlDocument();
            xmldoc.Load(url);

            XmlNodeList gameNodeList = xmldoc.GetElementsByTagName("game");
            foreach (XmlNode gameNode in gameNodeList)
            {
                try
                {
                    if (gameNode.Attributes["away_team_name"] != null)
                    {
                        Game game = new Game();
                        game.Id = GetAttribute(gameNode, "id", "NO_ID");
                        game.AmPm = GetAttribute(gameNode, "ampm", "");
                        game.PK = GetAttribute(gameNode, "game_pk", "");
                        game.StartTime = GetAttribute(gameNode, "time", "Unknown");
                        game.TimeZone = GetAttribute(gameNode, "time_zone", "ET");
                        game.Type = GetAttribute(gameNode, "game_type", "Unknown");
                        game.Day = GetAttribute(gameNode, "day", "UNK");
                        game.GamedaySW = GetAttribute(gameNode, "gameday_sw", "?");
                        game.League = GetAttribute(gameNode, "league", "Unknown");

                        game.Stadium = new Stadium();
                        game.Stadium.Name = GetAttribute(gameNode, "venue", "NO_NAME");
                        game.Stadium.VenueCode = GetAttribute(gameNode, "venue_w_chan_loc", "Unknown");

                        game.AwayTeam = ParseTeam(gameNode, true);
                        game.HomeTeam = ParseTeam(gameNode, false);

                        game.Status = ParseGameStatus(gameNode);
                        game.Linescore = ParseLinescore(gameNode.SelectSingleNode("linescore"), game.AwayTeam, game.HomeTeam);

                        XmlNode newsNode = gameNode.SelectSingleNode("newsroom");
                        if (newsNode != null)
                        {
                            XmlNode centerNode = newsNode.SelectSingleNode("gamecenter");
                            if (centerNode != null)
                            {
                                XmlNode gNode = centerNode.SelectSingleNode("game");
                                if (gNode != null)
                                {
                                    XmlNode probNode = gNode.SelectSingleNode("probables");
                                    if (probNode != null)
                                    {
                                        XmlNode probHomeNode = probNode.SelectSingleNode("home");
                                        game.HomeProbable = ParseProbable(probHomeNode);
                                        XmlNode probAwayNode = probNode.SelectSingleNode("away");
                                        game.AwayProbable = ParseProbable(probAwayNode);
                                    }
                                }
                            }
                        }
                        
                        m_calculator.SetRatio(game);
                        scoreBoard.GamesList.AddGame(game);
                        scoreBoard.ModeGameList[game.Status.Indicator.Category].AddGame(game);
                    }
                }
                catch { }
            }

            if (gameNodeList.Count > 0)
            {
                if (selectedGameId == null)
                {
                    scoreBoard.SelectedGame = scoreBoard.GamesList.GetHighestRatioGame();
                }
                else
                {
                    // replace selected game
                    foreach (Game excGame in scoreBoard.GamesList.Games)
                    {
                        if (excGame.Id == selectedGameId)
                        {
                            scoreBoard.SelectedGame = excGame;
                            break;
                        }
                    }
                }
            }
            xmldoc = null;
            return scoreBoard;
        }

        /// <summary>
        /// Create a probable pitcher
        /// </summary>
        private Probable ParseProbable(XmlNode node)
        {
            Probable probable = new Probable();
            string first = node.SelectSingleNode("useName").InnerText;
            string last = node.SelectSingleNode("lastName").InnerText;
            probable.Name = first + " " + last;

            probable.Wins = node.SelectSingleNode("wins").InnerText;
            probable.Losses = node.SelectSingleNode("losses").InnerText;
            probable.ERA = node.SelectSingleNode("era").InnerText;
            probable.Throwing = node.SelectSingleNode("throwinghand").InnerText;
            probable.Report = node.SelectSingleNode("report").InnerText;

            return probable;
        }

        /// <summary>
        /// Create a team with the xml information
        /// </summary>
        private Team ParseTeam(XmlNode node, bool away)
        {
            Team team = new Team();
            string prefix = null;
            if (away)
            {
                prefix = "away";
            }
            else
            {
                prefix = "home";
            }

            team.Id = GetAttribute(node, prefix + "_team_id", "NO_ID");
            team.Name = GetAttribute(node, prefix + "_team_name", "NO_NAME");
            team.Abbrv = GetAttribute(node, prefix + "_name_abbrev", "UNK");
            team.Code = GetAttribute(node, prefix + "_code", "UNK");
            team.FileCode = GetAttribute(node, prefix + "_file_code", "UNK");
            team.City = GetAttribute(node, prefix + "_team_city", "Unknown");
            team.Division = GetAttribute(node, prefix + "_division", "?");
            team.League = GetAttribute(node, prefix + "_league_id", "?");
            team.Sport = GetAttribute(node, prefix + "_sport_code", "?");
            team.GamesBack = GetAttribute(node, prefix + "_games_back", "-");
            team.GamesBackWildcard = GetAttribute(node, prefix + "_games_back_wildcard", "-");
            team.Wins = GetAttribute(node, prefix + "_win", 0);
            team.Losses = GetAttribute(node, prefix + "_loss", 0);
            return team;
        }

        /// <summary>
        /// Parse game status
        /// </summary>
        private GameStatus ParseGameStatus(XmlNode node)
        {
            GameStatus status = new GameStatus();

            XmlNode statusNode = node.SelectSingleNode("status");
            XmlNode batterNode = node.SelectSingleNode("batter");
            XmlNode pitcherNode = node.SelectSingleNode("pitcher");
            XmlNode opposingPitcherNode = node.SelectSingleNode("opposing_pitcher");
            XmlNode pbpNode = node.SelectSingleNode("pbp");
            XmlNode onDeckNode = node.SelectSingleNode("ondeck");
            XmlNode inHoleNode = node.SelectSingleNode("inhole");
            
            XmlNode homeRunsNode = node.SelectSingleNode("home_runs");
            XmlNode winningPitcherNode = node.SelectSingleNode("winning_pitcher");
            XmlNode losingPitcherNode = node.SelectSingleNode("losing_pitcher");
            XmlNode savePitcherNode = node.SelectSingleNode("save_pitcher");

            XmlNode runnersNode = node.SelectSingleNode("runners_on_base");
            XmlNode firstBaseNode = null;
            XmlNode secondBaseNode = null;
            XmlNode thirdBaseNode = null;
            if (runnersNode != null)
            {
                firstBaseNode = runnersNode.SelectSingleNode("runner_on_1b");
                secondBaseNode = runnersNode.SelectSingleNode("runner_on_2b");
                thirdBaseNode = runnersNode.SelectSingleNode("runner_on_3b");
            }

            status.Status = GetAttribute(statusNode, "status", "Unknown");
            status.Indicator = Indicators.GetIndicator(GetAttribute(statusNode, "ind", "?"));
            status.Reason = GetAttribute(statusNode, "reason", "");
            status.Inning = GetAttribute(statusNode, "inning", -1);
            status.InningTop = GetBoolAttribute(statusNode, "top_inning", "Y");
            status.Outs = GetAttribute(statusNode, "o", 0);
            status.Balls = GetAttribute(statusNode, "b", 0);
            status.Strikes = GetAttribute(statusNode, "s", 0);

            if (homeRunsNode != null)
            {
                XmlNodeList homeruns_list = homeRunsNode.SelectNodes("player");
                foreach (XmlNode homerunNode in homeruns_list)
                {
                    HomeRun homerun = new HomeRun();
                    homerun.Player = m_playersParser.ParsePlayer(homerunNode);
                    homerun.HR = GetAttribute(homerunNode, "hr", 0);
                    homerun.Inning = GetAttribute(homerunNode, "inning", 0);
                    homerun.Runners = GetAttribute(homerunNode, "runners", 0);
                    homerun.StdHR = GetAttribute(homerunNode, "std_hr", 0);
                    homerun.TeamCode = GetAttribute(homerunNode, "team_code", "NO_CODE");
                    status.AddHomeRun(homerun);
                }
            }

            status.CurrentBatter = m_playersParser.ParsePlayer(batterNode);
            status.CurrentPitcher = m_playersParser.ParsePlayer(pitcherNode);
            status.OppositePitcher = m_playersParser.ParsePlayer(opposingPitcherNode);
            if (pbpNode != null)
            {
                status.LastPbp = GetAttribute(pbpNode, "last", null);
            }
            status.OnDeck = m_playersParser.ParsePlayer(onDeckNode);
            status.InHole = m_playersParser.ParsePlayer(inHoleNode);
            status.WinningPitcher = m_playersParser.ParsePlayer(winningPitcherNode);
            status.LosingPitcher = m_playersParser.ParsePlayer(losingPitcherNode);
            status.SavingPitcher = m_playersParser.ParsePlayer(savePitcherNode);
            status.RunnersOnBase[CoreConstants.FIRST_BASE] = m_playersParser.ParsePlayer(firstBaseNode);
            status.RunnersOnBase[CoreConstants.SECOND_BASE] = m_playersParser.ParsePlayer(secondBaseNode);
            status.RunnersOnBase[CoreConstants.THIRD_BASE] = m_playersParser.ParsePlayer(thirdBaseNode);

            return status;
        }

        /// <summary>
        /// Parse game linescore
        /// </summary>
        private Linescore ParseLinescore(XmlNode node, Team awayTeam, Team homeTeam)
        {
            if (node != null)
            {
                Linescore linescore = new Linescore(awayTeam, homeTeam);
                XmlNodeList innings_list = node.SelectNodes("inning");
                XmlNode runs_node = node.SelectSingleNode("r");
                XmlNode hits_node = node.SelectSingleNode("h");
                XmlNode errors_node = node.SelectSingleNode("e");
                linescore.GetAwayLineScore().Runs = GetAttribute(runs_node, "away", 0);
                linescore.GetHomeLineScore().Runs = GetAttribute(runs_node, "home", 0);
                linescore.Diff = GetAttribute(runs_node, "diff", 0);
                linescore.GetAwayLineScore().Hits = GetAttribute(hits_node, "away", 0);
                linescore.GetHomeLineScore().Hits = GetAttribute(hits_node, "home", 0);
                linescore.GetAwayLineScore().Errors = GetAttribute(errors_node, "away", 0);
                linescore.GetHomeLineScore().Errors = GetAttribute(errors_node, "home", 0);
                foreach (XmlNode inningNode in innings_list)
                {
                    linescore.AddInning(GetAttribute(inningNode, "away", -1),
                        GetAttribute(inningNode, "home", -1));
                }
                return linescore;
            }
            else
            {
                return null;
            }
        }

        /// <summary>
        /// return the attribute in a safe way
        /// </summary>
        private string GetAttribute(XmlNode node, string name, string ifNullValue)
        {
            if (node.Attributes[name] == null)
            {
                return ifNullValue;
            }
            else
            {
                return node.Attributes[name].InnerText;
            }
        }

        /// <summary>
        /// return the attribute in a safe way
        /// </summary>
        private int GetAttribute(XmlNode node, string name, int ifNullValue)
        {
            if (node.Attributes[name] == null)
            {
               return ifNullValue;
            }
            else
            {
                try
                {
                    return int.Parse(node.Attributes[name].InnerText);
                }
                catch
                {
                     return ifNullValue;
                }
            }
        }

        /// <summary>
        /// return the attribute in a safe way
        /// </summary>
        private float GetAttribute(XmlNode node, string name, float ifNullValue)
        {
            if (node.Attributes[name] == null)
            {
                return ifNullValue;
            }
            else
            {
                try
                {
                    return float.Parse(node.Attributes[name].InnerText);
                }
                catch
                {
                    return ifNullValue;
                }
            }
        }

        /// <summary>
        /// return the attribute in a safe way
        /// </summary>
        private bool GetBoolAttribute(XmlNode node, string name, string condition)
        {
            if (node.Attributes[name] == null)
            {
                return false;
            }
            else
            {
                try
                {
                    string check = node.Attributes[name].InnerText;
                    return check.Equals(condition);
                }
                catch
                {
                    return false;
                }
            }
        }

        private class RawTable
        {
            string m_id = "";

            public string Id
            {
                get { return m_id; }
                set { m_id = value; }
            }
            List<string> m_rows = new List<string>();

            public List<string> Rows
            {
                get { return m_rows; }
                set { m_rows = value; }
            }
        }
    }
}
